#include "reportnavigator.h"

#include "../documents/swpprojectdocument.h"
#include "../mainwindow.h"
#include "../utils/mimetypemanager.h"
#include "../swpcommon.h"

#include <coremimetypes.h>
#include <iconmanager.h>
#include <ilinkeddocument.h>
#include <lineeditdelegate.h>
#include <saveevent.h>
#include <validatormanager.h>

#include <QtCore/QEvent>
#include <QtCore/QStringList>
#include <QtGui/QAction>
#include <QtGui/QContextMenuEvent>
#include <QtGui/QDragEnterEvent>
#include <QtGui/QDragMoveEvent>
#include <QtGui/QMenu>
#include <QtGui/QToolBar>

using namespace core;
using namespace gui;
using namespace swp;

typedef QSharedPointer<IDocument> Document;

ReportNavigator::ReportNavigator(QWidget* parent) :
    QTreeWidget(parent),
    IEditor(),
    m_itemToDocumentMapping(),
    m_contextMenu(0),
    m_closeAction(0),
    m_renameAction(0),
    m_removeAction(0),
    m_deleteAction(0),
    m_collapseAll(0),
    m_expandAll(0)
{
    LineEditDelegate* delegate = new LineEditDelegate;
    delegate->setRole(Qt::UserRole);
    delegate->setValidator(ValidatorManager::getInstance()->getValidator(
        DOCUMENT_NAME_VALIDATOR));

    headerItem()->setHidden(true);
    setDragEnabled(true);
    setDragDropMode(QAbstractItemView::InternalMove);
    setItemDelegate(delegate);
    setEditTriggers(QAbstractItemView::EditKeyPressed);

    createActions();
    connectSlots();
}

ReportNavigator::~ReportNavigator()
{

}

QSharedPointer<IDocument> ReportNavigator::document() const
{
    QSharedPointer<IDocument> ret;

    QTreeWidgetItem* item = currentItem();
    if (item != 0)
    {
        if (m_itemToDocumentMapping.contains(item))
        {
            ret = m_itemToDocumentMapping[item];
        }
        else
        {
            ret = m_itemToDocumentMapping[item->parent()];
        }
    }

    return ret;
}

QWidget* ReportNavigator::widget() const
{
    return const_cast<ReportNavigator* >(this);
}

QStringList ReportNavigator::galleryGroups() const
{
    return QStringList();
}

QList<GalleryItem> ReportNavigator::galleryItems(
    const QString& group) const
{
    return QList<GalleryItem>();
}

void ReportNavigator::registerFocusListener(QObject* object)
{
    // do nothing
}

void ReportNavigator::unregisterFocusListener(QObject* object)
{
    // do nothing
}

void ReportNavigator::addWindowMenuActions()
{
    //MainWindow* mainWindow = MainWindow::getInstance();

    //mainWindow->getMenu(PROJECT_MENU)->addSeparator();
    //mainWindow->getMenu(PROJECT_MENU)->addAction(exportAction); ??
}

void ReportNavigator::addToolBarActions(QToolBar* toolbar)
{

}

bool ReportNavigator::event(QEvent* event)
{
    bool ret = false;

    if (event->type() == SaveEvent::type())
    {
        saveSelectedProject();
        event->accept();
        ret = true;
    }
    else
    {
        ret = QTreeWidget::event(event);
    }

    return ret;
}

QList<QAction* > ReportNavigator::perspectiveEnsuringActions() const
{
    QList<QAction* > ret;

    return ret;
}

IEditor* ReportNavigator::copy() const
{
    return new ReportNavigator();
}

void ReportNavigator::contextMenuEvent(QContextMenuEvent* event)
{
    if (m_contextMenu == 0)
    {
        m_contextMenu = new QMenu(this);
    }

    foreach (QTreeWidgetItem* item, m_itemToDocumentMapping.keys())
    {
        if (visualItemRect(item).contains(event->pos()))
        {
            m_contextMenu->clear();

            const Document& doc =  m_itemToDocumentMapping[
                    item];
            const bool isContainer = doc->isContainer();
            const bool isOpened = !doc->isFuture();

            if (!isContainer)
            {
                m_contextMenu->addAction(m_renameAction);
                m_contextMenu->addSeparator();

                m_contextMenu->addAction(m_closeAction);
                m_closeAction->setEnabled(isOpened &&
                    !DocumentManager::getInstance()->isOpenedToMemory(doc));
                m_contextMenu->addAction(m_removeAction);
            }

            if (isContainer)
            {
                m_contextMenu->addSeparator();
                m_contextMenu->addAction(m_expandAll);
                m_contextMenu->addAction(m_collapseAll);
            }

            if (!isContainer)
            {
                m_contextMenu->addSeparator();
                m_contextMenu->addAction(m_deleteAction);
            }

            m_contextMenu->exec(event->globalPos());

            break;
        }
    }
}

void ReportNavigator::createActions()
{
    m_closeAction = new QAction(tr("Close"), this);
    m_renameAction = new QAction(tr("Rename"),
        this);

    m_removeAction = new QAction(tr("Remove"), this);
    m_deleteAction = new QAction(tr("Delete"), this);

    m_collapseAll = new QAction(tr("Collapse all"), this);
    m_expandAll = new QAction(tr("Expand all"), this);
}

void ReportNavigator::connectSlots()
{
    connect(this, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
        this, SLOT(openDocument(QTreeWidgetItem*,int)));

    connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
        this, SLOT(ensureDataConsistency(QTreeWidgetItem*,int)));

    connect(this, SIGNAL(selectionDeferred(QTreeWidgetItem*)),
            this, SLOT(selectItem(QTreeWidgetItem*)), Qt::QueuedConnection);

    connect(m_collapseAll, SIGNAL(triggered()),
        this, SLOT(collapseAll()));
    connect(m_expandAll, SIGNAL(triggered()),
        this, SLOT(expandAll()));

    connect(m_closeAction, SIGNAL(triggered()),
        this, SLOT(closeSelected()));

    connect(m_renameAction, SIGNAL(triggered()),
        this, SLOT(renameSelected()));

    connect(m_removeAction, SIGNAL(triggered()),
            this, SLOT(removeDocument()));
    connect(m_deleteAction, SIGNAL(triggered()),
            this, SLOT(deleteDocument()));
}

// public slots
void ReportNavigator::open(const QSharedPointer<IDocument>& document)
{
    Q_ASSERT(!document.isNull() && document->isContainer());

    IconManager* iconManager = IconManager::getInstance();

    QSharedPointer<SwpProjectDocument> _document =
        qSharedPointerDynamicCast<SwpProjectDocument>(document);

    Q_ASSERT(!document.isNull());

    QTreeWidgetItem* projectItem = new QTreeWidgetItem(
        QStringList(QString("%1%2").arg(_document->name()).arg(
            _document->isDirty() ? "*" : "")));
    projectItem->setIcon(0, iconManager->icon(
        _document->mimeType().iconName()));
    projectItem->setData(0, Qt::UserRole, _document->name());
    projectItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable |
        Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEnabled);

    connect(document.data(), SIGNAL(dirtyChanged(bool)),
        this, SLOT(ensureDocumentNameConsistency()));

    m_itemToDocumentMapping[projectItem] = document;

    addTopLevelItem(projectItem);

    const core::DocumentCategories categories = _document->categories();

    MimeTypeManager* mm = MimeTypeManager::getInstance();

    foreach (const QString& category, categories)
    {
        const QList<MimeType> _mimeTypes =
            _document->categoryMimeTypes(category);
        if (_mimeTypes.contains(mm->mimeType(REPORT_MIMETYPE)))
        {
            QTreeWidgetItem* categoryItem = new QTreeWidgetItem(
                QStringList(category));
            categoryItem->setIcon(0, iconManager->icon("SwpProjectCategory"));
            categoryItem->setFlags(Qt::ItemIsEnabled);
            projectItem->addChild(categoryItem);
            categoryItem->setExpanded(true);

            QList<QSharedPointer<IDocument> > childDocuments =
                _document->documents(category);

            foreach (const QSharedPointer<IDocument>& childDocument,
                childDocuments)
            {
                QTreeWidgetItem* documentItem = new QTreeWidgetItem(
                    QStringList(QString("%1%2").arg(childDocument->name()).arg(
                        childDocument->isDirty() ? "*" : "")));
                documentItem->setIcon(0, iconManager->icon(
                    childDocument->mimeType().iconName()));
                documentItem->setData(0, Qt::UserRole, childDocument->name());
                documentItem->setFlags(Qt::ItemIsSelectable |
                    Qt::ItemIsEditable | Qt::ItemIsDragEnabled |
                    Qt::ItemIsEnabled);

                m_itemToDocumentMapping[documentItem] = childDocument;

                categoryItem->addChild(documentItem);

                connect(childDocument.data(), SIGNAL(dirtyChanged(bool)),
                    this, SLOT(ensureDocumentNameConsistency()));
            }
        }
    }

    setCurrentItem(projectItem);
    scrollToItem(projectItem);
    expandItem(projectItem);
}

void ReportNavigator::add(
    const QSharedPointer<core::IDocument>& project,
    const QSharedPointer<core::IDocument>& document)
{
    const QSharedPointer<IContainerDocument>& _project =
        qSharedPointerDynamicCast<IContainerDocument>(project);

    foreach (QTreeWidgetItem* item,
        m_itemToDocumentMapping.keys())
    {
        if (project == m_itemToDocumentMapping[item])
        {
            item->setText(0, QString("%1%2").arg(project->name()).arg(
                              project->isDirty() ? "*" : ""));

            for (int i = 0; i < item->childCount(); ++i)
            {
                QTreeWidgetItem* category = item->child(i);
                if (category->text(0) == _project->categoryFor(document))
                {
                    QTreeWidgetItem* documentItem = new QTreeWidgetItem(
                        QStringList(QString("%1%2").arg(document->name()).arg(
                            document->isDirty() ? "*" : "")));
                    documentItem->setIcon(0, IconManager::getInstance()->icon(
                        document->mimeType().iconName()));
                    documentItem->setData(0, Qt::UserRole, document->name());
                    documentItem->setFlags(Qt::ItemIsSelectable |
                        Qt::ItemIsEditable | Qt::ItemIsDragEnabled |
                        Qt::ItemIsEnabled);

                    m_itemToDocumentMapping[documentItem] = document;

                    category->addChild(documentItem);
                    category->setExpanded(true);

                    setCurrentItem(documentItem);

                    connect(document.data(), SIGNAL(dirtyChanged(bool)),
                        this, SLOT(ensureDocumentNameConsistency()));

                    break;
                }
            }

            break;
        }
    }
}

void ReportNavigator::remove(
    const QSharedPointer<core::IDocument>& project,
    const QSharedPointer<core::IDocument>& document)
{
    foreach (QTreeWidgetItem* item,
        m_itemToDocumentMapping.keys())
    {
        if (document == m_itemToDocumentMapping[item])
        {
            m_itemToDocumentMapping.remove(item);

            disconnect(document.data(), SIGNAL(dirtyChanged(bool)),
                this, SLOT(ensureDocumentNameConsistency()));

            delete item;

            break;
        }
    }
}

void ReportNavigator::remove(
    const QSharedPointer<core::IDocument>& document)
{
    foreach (QTreeWidgetItem* item,
        m_itemToDocumentMapping.keys())
    {
        if (document == m_itemToDocumentMapping[item])
        {
            m_itemToDocumentMapping.remove(item);

            disconnect(document.data(), SIGNAL(dirtyChanged(bool)),
                this, SLOT(ensureDocumentNameConsistency()));

            delete item;

            break;
        }
    }
}

void ReportNavigator::close(
    const QSharedPointer<core::IDocument>& document)
{
    foreach (QTreeWidgetItem* item,
        m_itemToDocumentMapping.keys())
    {
        if (document == m_itemToDocumentMapping[item])
        {
            if (document->isContainer())
            {
                for (int i = 0; i < topLevelItemCount(); ++i)
                {
                    if (item == topLevelItem(i))
                    {
                        takeTopLevelItem(i);
                        break;
                    }
                }

                QSharedPointer<SwpProjectDocument> _document =
                    qSharedPointerDynamicCast<SwpProjectDocument>(document);

                foreach (const QString& category, _document->categories())
                {
                    foreach (const QSharedPointer<IDocument>& childDocument,
                        _document->documents(category))
                    {
                        disconnect(childDocument.data(),
                            SIGNAL(dirtyChanged(bool)),
                            this, SLOT(ensureDocumentNameConsistency()));
                    }
                }

                disconnect(document.data(),
                    SIGNAL(dirtyChanged(bool)),
                    this, SLOT(ensureDocumentNameConsistency()));

                m_itemToDocumentMapping.remove(item);
            }

            break;
        }
    }
}

void ReportNavigator::tryToSelectDocument(
    const QSharedPointer<IDocument>& document)
{
    foreach (QTreeWidgetItem* item, m_itemToDocumentMapping.keys())
    {
        if (m_itemToDocumentMapping[item] == document)
        {
            selectItem(item);
            break;
        }
    }
}

void ReportNavigator::closeSelected()
{
    QTreeWidgetItem* selection = currentItem();

    if (m_itemToDocumentMapping.contains(selection))
    {
        emit aboutToClose(m_itemToDocumentMapping[selection]);
    }
}

void ReportNavigator::renameSelected()
{
    QTreeWidgetItem* selection = currentItem();

    if (m_itemToDocumentMapping.contains(selection))
    {
        editItem(selection);
    }
}

void ReportNavigator::saveSelectedProject()
{
    QTreeWidgetItem* selection = currentItem();

    if (m_itemToDocumentMapping.contains(selection))
    {
        QSharedPointer<IDocument> document = m_itemToDocumentMapping[
            selection];

        if (!document->isContainer())
        {
            document = qSharedPointerDynamicCast<IDocument>(
                document->container());
        }

        emit aboutToSave(document);
    }
    else
    {
        QTreeWidgetItem* parent = selection->parent();

        if (m_itemToDocumentMapping.contains(parent))
        {
            emit aboutToSave(m_itemToDocumentMapping[parent]);
        }
    }
}

// private slots
void ReportNavigator::removeDocument()
{
    QTreeWidgetItem* selection = currentItem();

    if (selection != 0)
    {
        QSharedPointer<IDocument> project;
        QSharedPointer<IDocument> document;

        if (m_itemToDocumentMapping.contains(selection))
        {
            document = m_itemToDocumentMapping[
                selection];

            project = m_itemToDocumentMapping[selection->parent()->parent()];

            emit aboutToRemove(project, document);
        }
    }
}

void ReportNavigator::deleteDocument()
{
    QTreeWidgetItem* selection = currentItem();

    if (selection != 0)
    {
        emit aboutToDelete(m_itemToDocumentMapping[selection]);
    }
}

void ReportNavigator::openDocument(QTreeWidgetItem* item, int column)
{
    if (m_itemToDocumentMapping.contains(item))
    {
        QSharedPointer<IDocument>& document = m_itemToDocumentMapping[item];

        if (!document->isContainer() &&
            !DocumentManager::getInstance()->isOpenedToMemory(document))
        {
            emit aboutToOpen(document);
        }
    }
}

void ReportNavigator::ensureDataConsistency(QTreeWidgetItem* item,
    int column)
{
    if (m_itemToDocumentMapping.contains(item))
    {
        QSharedPointer<IDocument>& document = m_itemToDocumentMapping[item];

        const QString name = item->data(0, Qt::UserRole).toString();
        if (document->name() != name)
        {
            document->rename(item->data(0, Qt::UserRole).toString());
            document->setDirty();
            item->setText(0, QString("%1%2").arg(document->name()).arg(
                document->isDirty() ? "*" : ""));
            item->setData(0, Qt::UserRole, document->name());
        }
    }
}

void ReportNavigator::ensureDocumentNameConsistency()
{
    foreach (QTreeWidgetItem* item, m_itemToDocumentMapping.keys())
    {
        const QSharedPointer<IDocument> document =
            m_itemToDocumentMapping[item];

        item->setText(0, QString("%1%2").arg(document->name()).arg(
            document->isDirty() ? "*" : ""));
    }
}

void ReportNavigator::selectItem(QTreeWidgetItem* item)
{
    if (item != 0)
    {
        setCurrentItem(item);
    }
}
